Optional Tasks.f

{
Saving/restoring game state
  Easiest way: save the entire dictionary starting from ORIGIN in a file, with an entry point in the first cell probably.
  Would need to save buffers too.
}

Requires Tools
Requires Dorothy
Only Forth Definitions

[undefined] default-tasks-dstack-size [if] 512 3 / cell/ Constant default-tasks-dstack-size [then]
[undefined] default-tasks-rstack-size [if] 512 3 / cell/ Constant default-tasks-rstack-size [then]

default-tasks-dstack-size 8 - cells Constant dstack-start
default-tasks-rstack-size 8 - cells Constant rstack-start

node extend: task
   int.field sp
   int.field rp
   default-tasks-dstack-size int.field[] dstack
   default-tasks-rstack-size int.field[] rstack
end-type

node extend: ring end-type

0 task.ref mind

code pause   ( -- )
   PUSH(EBX)
   &of mind [u] ebx mov
   'lf [u] push         \ save locals frame
   &of (this) [u] push
   ebp 0 task.sp [ebx] mov
   0 [ebx] ecx mov      \ get next task
   esp 0 task.rp [ebx] mov
    ecx ecx or  0= if
      16 [ebx] ebx mov  \ parent
      8 [ebx] ecx mov   \ go back to first task of ring
   then
   0 task.rp [ecx] esp mov     \ get stack pointers
   0 task.sp [ecx] ebp mov
   &of (this) [u] pop
   'lf [u] pop          \ restore locals frame
   ecx &of mind [u] mov \ store new task pointer
   POP(EBX)
   ret end-code

code unpause
   0 task.rp [ebx] esp mov     \ get stack pointers
   0 task.sp [ebx] ebp mov
   blk [u] pop
   'lf [u] pop              \ restore locals frame
   ebx &of mind [u] mov     \ store new task pointer
   POP(EBX)
   ret end-code

:[] task.act:[   ( val task xt -- )
   >code swap ( val code task )
   <<
      rstack rstack-start + cell- cell- rp !     \ initialize return stack
      \ rstack rstack-start + rp !     \ initialize return stack
      dstack dstack-start + sp !           \ initialize data stack
      rp @ cell+ cell+ !                       \ set return
      \ rp @ !                            \ set return
      sp @ !                             \ set tos
      mind this = if
         rp @ rp! sp @ sp! exit         \ case for task assigning itself
      then
   >> ;

method: task.peek
   cr ." Data stack: "
   sp @ begin cr space @+ . dup dstack dstack-start + > until drop
   cr ." Return stack: "
   rp @ begin cr space @+ . dup rstack rstack-start + > until drop ;


: task.halt   0 swap act:[ begin pause again ;] ;

: doze   begin pause again ;

: pauses  0 do pause loop ;

: end
   mind.next
   mind.remove
   mind.dispose
   unpause ;

: task.construct   ( ring task -- )
   dup  halt  dup  rot  add ;
' task.construct >delegate task construct

: ring.jolt ( ring -- )
   first ?dup 0= ?exit
   mind >q
   sp@ >q
   rp@ >q
      to mind pause
   q> rp!
   q> sp!
   q> to mind
   drop ;

\ Rings need an extra task at the beginning so JOLT can save and restore the caller's state.
: ring.init  task.new drop ;

: Ring:   ring.make  lastbody @ ring.init  ;
: Task:   task.make  lastbody @ halt ;


\\
\ Initialize mind to the app-level task and attach it to a default ring:
Ring: main
Task: god
god main.add
god to mind
pause


\ Test
\\

Ring: Jack
Ring: Queen

variable n

: test   new-task 0 swap act:[ begin 10 0 do i n +! pause loop again ;] ;
: test2   new-task 0 swap act:[ begin jack jolt pause again ;] ;

jack test
queen test2
queen jolt n ?
queen jolt n ?
main-ring test2
pause n ?


